/*
 * Licensed under the MIT License
 * 
 * Copyright (c) 2011 Kay Kreuning
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * 
 * http://code.google.com/p/hyves-as3-sdk/
 */
package nl.hyves.api.utils
{
	import nl.hyves.api.core.Consumer;
	import nl.hyves.api.core.OAuthToken;
	import nl.hyves.api.methods.IMethod;

	import com.adobe.crypto.HMAC;
	import com.adobe.crypto.SHA1;

	import mx.utils.Base64Encoder;

	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.utils.ByteArray;

	public class AuthUtil
	{
		private static const HA_VERSION:String = '2.0';
		private static const HA_FORMAT:String = 'json';
		private static const HA_FANCYLAYOUT:String = 'false';
		private static const HA_RESPONSECODE_ALWAYS_200:String = 'true';
		private static const SIGNATURE_METHOD:String = 'HMAC-SHA1';
		private static const HTTP_METHOD:String = URLRequestMethod.POST;
		
		private static const encoder:Base64Encoder = new Base64Encoder();
		
		public static function buildRequest(parameters:Object, url:String, consumer:Consumer, token:OAuthToken):URLRequest
		{
			if (token && token.oauth_token)
			{
				parameters['oauth_token'] = token.oauth_token;
			}

			parameters['oauth_nonce'] = int(Math.random() * 0x7FFFFFFF).toString();
			parameters['oauth_timestamp'] = int(new Date().getTime() * 0.001 + 0.5).toString();
			parameters['oauth_consumer_key'] = consumer.key;
			parameters['oauth_signature_method'] = SIGNATURE_METHOD;
			parameters['oauth_signature'] = calculateSignature(parameters, url, consumer.secret, token ? token.oauth_token_secret : "");

			var decodedParameters:String = decodeParameters(parameters);

			var request:URLRequest = new URLRequest();
			request.method = HTTP_METHOD;
			request.url = url;
			request.data = decodedParameters;

			return request;
		}

		public static function extractToken(response:Object):OAuthToken
		{
			var token:OAuthToken = new OAuthToken();
			token.oauth_token = response.oauth_token;
			token.oauth_token_secret = response.oauth_token_secret;
			token.userid = response.hasOwnProperty('userid') ? response.userid : '';
			token.methods = response.hasOwnProperty('methods') ? response.methods : null;
			token.expiredate = response.hasOwnProperty('expiredate') ? response.expiredate : null;

			return token;
		}

		public static function hash(string:String):String
		{
			return SHA1.hash(string);
		}

		public static function buildParameters(method:Object, parameters:Object = null):Object
		{
			parameters ||= new Object();

			if (method is IMethod)
			{
				for (var p:String in method.parameters)
				{
					parameters[p] = method.parameters[p];
				}
			}

			parameters['ha_method'] ||= method;
			parameters['ha_version'] ||= HA_VERSION;
			parameters['ha_format'] ||= HA_FORMAT;
			parameters['ha_fancylayout'] ||= HA_FANCYLAYOUT;
			parameters['ha_responsecode_always_200'] = HA_RESPONSECODE_ALWAYS_200;

			return parameters;
		}

		private static function decodeParameters(parameters:Object):String
		{
			var array:Array = new Array();

			for (var key:String in parameters)
			{
				array.push(key + '=' + encode(parameters[key].toString()));
			}

			array.sort();

			return array.join('&');
		}

		private static function calculateSignature(parameters:Object, url:String, consumerSecret:String, tokenSecret:String):String
		{
			var secret:String = encodeURIComponent(consumerSecret) + '&' + encodeURIComponent(tokenSecret || "");
			var message:String = HTTP_METHOD + '&' + encodeURIComponent(url) + '&' + encodeURIComponent(decodeParameters(parameters));
			
			var hashBytes:ByteArray = new ByteArray();
			var hash:String = HMAC.hash(secret, message, SHA1);
			
			for (var i : int = 0; i < hash.length; i += 2)
			{
				hashBytes.writeByte(parseInt(hash.charAt(i) + hash.charAt(i + 1), 0x10));
			}
			
			encoder.encodeBytes(hashBytes);
			
			return encoder.toString();
		}

		private static function encode(string:String):String
		{
			string = encodeURIComponent(string);
			string = string.replace(/\!/g, '%21');
			string = string.replace(/\*/g, '%2A');
			string = string.replace(/\'/g, '%27');
			string = string.replace(/\(/g, '%28');
			string = string.replace(/\)/g, '%29');

			return string;
		}
	}
}